// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef RS_HH
# define RS_HH

# include "signal.hh"

namespace funky {

  template <typename Set, typename Reset>
  struct rs_signal {
  private:
    typedef typename signal_traits<Set>::duration duration;
    typedef typename signal_traits<Set>::sample sample;
    Set set;
    Reset reset;
    sample state;

  public:
    template <typename T, typename U>
    rs_signal(T&& set, U&& reset)
      : set(std::forward<T>(set)),
	reset(std::forward<U>(reset)), state() {
    }

    rs_signal(const rs_signal&) = default;
    rs_signal(rs_signal&& other)
      : set(std::move(other.set)),
	reset(std::move(other.reset)),
	state(other.state) {}

    rs_signal& operator=(const rs_signal&other) {
      return *this = rs_signal(other);
    }

    rs_signal& operator=(rs_signal&& other) {
      std::swap(set, other.set);
      std::swap(reset, other.reset);
      std::swap(state, other.state);
      return *this;
    }

    rs_signal& operator+=(const duration& d) {
      set += d;
      reset += d;
      return *this;
    }

    rs_signal& operator++() {
      ++set;
      ++reset;
      return *this;
    }

    sample operator*() const {
      return **const_cast<rs_signal*>(this);
    }

    sample operator*() {
      state |= *set;
      state.flip();
      state |= *reset;
      state.flip();
      return state;
    }
  };

  template <typename Set, typename Reset>
  struct signal_traits<rs_signal<Set, Reset> > {
    typedef typename signal_traits<Set>::duration duration;
    typedef typename signal_traits<Set>::sample sample;
  };

  template <typename T, typename U,
	    typename RT = typename std::decay<T>::type,
	    typename RU = typename std::decay<U>::type,
	    typename = typename signal_traits<RT>::sample,
	    typename = typename signal_traits<RU>::sample>
  rs_signal<RT, RU> rs(T&& set, U&& reset) {
    return rs_signal<RT, RU>{set, reset};
  }

}

#endif
